package parser_antlr4.mysql;

import com.alibaba.druid.sql.ast.SQLCommentHint;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLPartitionBy;
import com.alibaba.druid.sql.ast.expr.SQLBetweenExpr;
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlExtPartition;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlCreateTableParser;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser;
import com.alibaba.druid.sql.parser.*;
import com.alibaba.druid.util.FnvHash;
import com.alibaba.druid.util.MySqlUtils;

public class MySQLCreateParser extends MySqlCreateTableParser {
  public MySQLCreateParser(String sql) {
    super(sql);
  }

  public MySQLCreateParser(SQLExprParser exprParser) {
    super(exprParser);
  }


  @Override
  public SQLCreateTableParser getSQLCreateTableParser() {
    return new MySQLCreateParser(this.exprParser);
  }

  @Override
  public MySqlCreateTableStatement parseCreateTable(boolean acceptCreate) {
    MySqlCreateTableStatement stmt = new MySqlCreateTableStatement();
    if (acceptCreate) {
      if (lexer.hasComment() && lexer.isKeepComments()) {
        stmt.addBeforeComment(lexer.readAndResetComments());
      }
      accept(Token.CREATE);
    }

    if (lexer.identifierEquals("TEMPORARY")) {
      lexer.nextToken();
      stmt.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY);
    } else if (lexer.identifierEquals("SHADOW")) {
      lexer.nextToken();
      stmt.setType(SQLCreateTableStatement.Type.SHADOW);
    }

    if (lexer.identifierEquals(FnvHash.Constants.DIMENSION)) {
      lexer.nextToken();
      stmt.setDimension(true);
    }

    if (lexer.token() == Token.HINT) {
      this.exprParser.parseHints(stmt.getHints());
    }

    if (lexer.identifierEquals(FnvHash.Constants.EXTERNAL)) {
      lexer.nextToken();
      stmt.setExternal(true);
    }

    accept(Token.TABLE);

    if (lexer.token() == Token.IF || lexer.identifierEquals("IF")) {
      lexer.nextToken();
      accept(Token.NOT);
      accept(Token.EXISTS);

      stmt.setIfNotExiists(true);
    }

    stmt.setName(this.exprParser.name());

    if (lexer.token() == Token.LIKE) {
      lexer.nextToken();
      SQLName name = this.exprParser.name();
      stmt.setLike(name);
    }

    if (lexer.token() == Token.WITH) {
      SQLSelect query = new MySqlSelectParser(this.exprParser).select();
      stmt.setSelect(query);
    } else if (lexer.token() == (Token.LPAREN)) {
      lexer.nextToken();

      if (lexer.token() == Token.SELECT) {
        SQLSelect query = new MySqlSelectParser(this.exprParser).select();
        stmt.setSelect(query);
      } else {
        for (; ; ) {
          SQLColumnDefinition column = null;

          boolean global = false;
          if (lexer.identifierEquals(FnvHash.Constants.GLOBAL)) {
            final Lexer.SavePoint mark = lexer.mark();
            lexer.nextToken();
            if (lexer.token() == Token.INDEX || lexer.token() == Token.UNIQUE) {
              global = true;
            } else {
              lexer.reset(mark);
            }
          }

          boolean local = false;
          if (lexer.identifierEquals(FnvHash.Constants.LOCAL)) {
            final Lexer.SavePoint mark = lexer.mark();
            lexer.nextToken();
            if (lexer.token() == Token.INDEX || lexer.token() == Token.KEY
                || lexer.token() == Token.UNIQUE) {
              local = true;
            } else if (lexer.token() == Token.FULLTEXT) {
              lexer.nextToken();

              if (lexer.token() == Token.KEY) {
                MySqlKey fulltextKey = new MySqlKey();
                this.exprParser.parseIndex(fulltextKey.getIndexDefinition());
                fulltextKey.setIndexType("FULLTEXT");
                fulltextKey.setParent(stmt);
                stmt.getTableElementList().add(fulltextKey);

                while (lexer.token() == Token.HINT) {
                  lexer.nextToken();
                }

                if (lexer.token() == Token.RPAREN) {
                  break;
                } else if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
                }
              } else if (lexer.token() == Token.INDEX) {
                MySqlTableIndex idx = new MySqlTableIndex();
                this.exprParser.parseIndex(idx.getIndexDefinition());
                idx.setIndexType("FULLTEXT");
                idx.setParent(stmt);
                stmt.getTableElementList().add(idx);

                if (lexer.token() == Token.RPAREN) {
                  break;
                } else if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
                }
              } else if (lexer.token() == Token.IDENTIFIER && MySqlUtils.isBuiltinDataType(
                  lexer.stringVal())) {
                lexer.reset(mark);
              } else {
                MySqlTableIndex idx = new MySqlTableIndex();
                this.exprParser.parseIndex(idx.getIndexDefinition());
                idx.setIndexType("FULLTEXT");
                idx.setParent(stmt);
                stmt.getTableElementList().add(idx);

                if (lexer.token() == Token.RPAREN) {
                  break;
                } else if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
                }
              }

            } else if (lexer.identifierEquals(FnvHash.Constants.SPATIAL)) {
              lexer.nextToken();
              if (lexer.token() == Token.INDEX || lexer.token() == Token.KEY ||
                  lexer.token() != Token.IDENTIFIER || !MySqlUtils.isBuiltinDataType(lexer.stringVal())) {
                MySqlTableIndex idx = new MySqlTableIndex();
                this.exprParser.parseIndex(idx.getIndexDefinition());
                idx.setIndexType("SPATIAL");
                idx.setParent(stmt);
                stmt.getTableElementList().add(idx);

                if (lexer.token() == Token.RPAREN) {
                  break;
                } else if (lexer.token() == Token.COMMA) {
                  lexer.nextToken();
                  continue;
                }
              } else {
                lexer.reset(mark);
              }
            } else {
              lexer.reset(mark);
            }
          }

          if (lexer.token() == Token.FULLTEXT) {
            Lexer.SavePoint mark = lexer.mark();
            lexer.nextToken();

            if (lexer.token() == Token.KEY) {
              MySqlKey fulltextKey = new MySqlKey();
              this.exprParser.parseIndex(fulltextKey.getIndexDefinition());
              fulltextKey.setIndexType("FULLTEXT");
              fulltextKey.setParent(stmt);
              stmt.getTableElementList().add(fulltextKey);

              while (lexer.token() == Token.HINT) {
                lexer.nextToken();
              }

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            } else if (lexer.token() == Token.INDEX) {
              MySqlTableIndex idx = new MySqlTableIndex();
              this.exprParser.parseIndex(idx.getIndexDefinition());
              idx.setIndexType("FULLTEXT");
              idx.setParent(stmt);
              stmt.getTableElementList().add(idx);

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            } else if (lexer.token() == Token.IDENTIFIER && MySqlUtils.isBuiltinDataType(lexer.stringVal())) {
              lexer.reset(mark);
            } else {
              MySqlTableIndex idx = new MySqlTableIndex();
              this.exprParser.parseIndex(idx.getIndexDefinition());
              idx.setIndexType("FULLTEXT");
              idx.setParent(stmt);
              stmt.getTableElementList().add(idx);

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            }

          } else if (lexer.identifierEquals(FnvHash.Constants.SPATIAL)) {
            Lexer.SavePoint mark = lexer.mark();
            lexer.nextToken();
            if (lexer.token() == Token.INDEX || lexer.token() == Token.KEY ||
                lexer.token() != Token.IDENTIFIER || !MySqlUtils.isBuiltinDataType(lexer.stringVal())) {
              MySqlTableIndex idx = new MySqlTableIndex();
              this.exprParser.parseIndex(idx.getIndexDefinition());
              idx.setIndexType("SPATIAL");
              idx.setParent(stmt);
              stmt.getTableElementList().add(idx);

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            } else {
              lexer.reset(mark);
            }
          }

          if (lexer.identifierEquals(FnvHash.Constants.ANN)) {
            Lexer.SavePoint mark = lexer.mark();
            lexer.nextToken();
            if (lexer.token() == Token.INDEX || lexer.token() == Token.KEY) {
              MySqlTableIndex idx = new MySqlTableIndex();
              this.exprParser.parseIndex(idx.getIndexDefinition());
              idx.setIndexType("ANN");
              idx.setParent(stmt);
              stmt.getTableElementList().add(idx);

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            } else {
              lexer.reset(mark);
            }
          }
          if (lexer.identifierEquals(FnvHash.Constants.CLUSTERED)) {
            lexer.nextToken();
            if (lexer.token() == Token.KEY) {
              MySqlKey clsKey = new MySqlKey();
              this.exprParser.parseIndex(clsKey.getIndexDefinition());
              clsKey.setIndexType("CLUSTERED");
              clsKey.setParent(stmt);
              stmt.getTableElementList().add(clsKey);

              if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            } else if (lexer.token() == Token.INDEX) {
              MySqlTableIndex idx = new MySqlTableIndex();
              this.exprParser.parseIndex(idx.getIndexDefinition());
              idx.setIndexType("CLUSTERED");
              idx.setParent(stmt);
              stmt.getTableElementList().add(idx);

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            }
          } else if (lexer.identifierEquals(FnvHash.Constants.CLUSTERING)) {
            lexer.nextToken();
            if (lexer.token() == Token.KEY) {
              MySqlKey clsKey = new MySqlKey();
              this.exprParser.parseIndex(clsKey.getIndexDefinition());
              clsKey.setIndexType("CLUSTERING");
              clsKey.setParent(stmt);
              stmt.getTableElementList().add(clsKey);

              if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            } else if (lexer.token() == Token.INDEX) {
              MySqlTableIndex idx = new MySqlTableIndex();
              this.exprParser.parseIndex(idx.getIndexDefinition());
              idx.setIndexType("CLUSTERING");
              idx.setParent(stmt);
              stmt.getTableElementList().add(idx);

              if (lexer.token() == Token.RPAREN) {
                break;
              } else if (lexer.token() == Token.COMMA) {
                lexer.nextToken();
                continue;
              }
            }
          } else if (lexer.token() == Token.IDENTIFIER //
              || lexer.token() == Token.LITERAL_CHARS) {
            column = this.exprParser.parseColumn();
            column.setParent(stmt);
            stmt.getTableElementList().add(column);

            if (lexer.isKeepComments() && lexer.hasComment()) {
              column.addAfterComment(lexer.readAndResetComments());
            }
          } else if (lexer.token() == Token.CONSTRAINT //
              || lexer.token() == Token.PRIMARY //
              || lexer.token() == Token.UNIQUE) {
            SQLTableConstraint constraint = this.parseConstraint();
            constraint.setParent(stmt);

            if (constraint instanceof MySqlUnique) {
              MySqlUnique unique = (MySqlUnique) constraint;
              if (global) {
                unique.setGlobal(true);
              }
              if (local) {
                unique.setLocal(true);
              }
            }

            stmt.getTableElementList().add(constraint);
          } else if (lexer.token() == (Token.INDEX)) {
            MySqlTableIndex idx = new MySqlTableIndex();
            this.exprParser.parseIndex(idx.getIndexDefinition());

            if (global) {
              idx.getIndexDefinition().setGlobal(true);
            }

            if (local) {
              idx.getIndexDefinition().setLocal(true);
            }

            idx.setParent(stmt);
            stmt.getTableElementList().add(idx);
          } else if (lexer.token() == (Token.KEY)) {
            Lexer.SavePoint savePoint = lexer.mark();
            lexer.nextToken();

            boolean isColumn = false;
            if (lexer.identifierEquals(FnvHash.Constants.VARCHAR)) {
              isColumn = true;
            }
            lexer.reset(savePoint);

            if (isColumn) {
              column = this.exprParser.parseColumn();
              stmt.getTableElementList().add(column);
            } else {
              stmt.getTableElementList().add(parseConstraint());
            }
          } else if (lexer.token() == (Token.PRIMARY)) {
            SQLTableConstraint pk = parseConstraint();
            pk.setParent(stmt);
            stmt.getTableElementList().add(pk);
          } else if (lexer.token() == (Token.FOREIGN)) {
            SQLForeignKeyConstraint fk = this.getExprParser().parseForeignKey();
            fk.setParent(stmt);
            stmt.getTableElementList().add(fk);
          } else if (lexer.token() == Token.CHECK) {
            SQLCheck check = this.exprParser.parseCheck();
            stmt.getTableElementList().add(check);
          } else if (lexer.token() == Token.LIKE) {
            lexer.nextToken();
            SQLTableLike tableLike = new SQLTableLike();
            tableLike.setTable(new SQLExprTableSource(this.exprParser.name()));
            tableLike.setParent(stmt);
            stmt.getTableElementList().add(tableLike);

            if (lexer.identifierEquals(FnvHash.Constants.INCLUDING)) {
              lexer.nextToken();
              acceptIdentifier("PROPERTIES");
              tableLike.setIncludeProperties(true);
            } else if (lexer.identifierEquals(FnvHash.Constants.EXCLUDING)) {
              lexer.nextToken();
              acceptIdentifier("PROPERTIES");
              tableLike.setExcludeProperties(true);
            }
          } else {
            column = this.exprParser.parseColumn();
            stmt.getTableElementList().add(column);
          }

          if (lexer.token() == Token.HINT) {
            lexer.nextToken();
          }

          if (lexer.token() != Token.COMMA) {
            break;
          } else {
            lexer.nextToken();
            if (lexer.isKeepComments() && lexer.hasComment() && column != null) {
              column.addAfterComment(lexer.readAndResetComments());
            }
          }
        }
      }

      if (lexer.token() == Token.HINT) {
        lexer.nextToken();
      }

      accept(Token.RPAREN);

      if (lexer.token() == Token.HINT && lexer.stringVal().charAt(0) == '!') {
        lexer.nextToken();
      }
    }

    for (; ; ) {
      if (lexer.token() == Token.COMMA) {
        lexer.nextToken();
      }

      if (lexer.identifierEquals(FnvHash.Constants.ENGINE)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = null;
        if (lexer.token() == Token.MERGE) {
          expr = new SQLIdentifierExpr(lexer.stringVal());
          lexer.nextToken();
        } else {
          expr = this.exprParser.expr();
        }
        stmt.setEngine(expr);
        continue;
      }

      if (lexer.identifierEquals("PAGE_CHECKSUM")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.expr();
        stmt.setPageChecksum(expr);
        continue;
      }

      if (lexer.identifierEquals("TRANSACTIONAL")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.expr();
        stmt.setTransactional(expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.BLOCK_SIZE)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = null;
        if (lexer.token() == Token.MERGE) {
          expr = new SQLIdentifierExpr(lexer.stringVal());
          lexer.nextToken();
        } else {
          expr = this.exprParser.integerExpr();
        }
        stmt.addOption("BLOCK_SIZE", expr);
        continue;
      }

      if (lexer.identifierEquals("BLOCK_FORMAT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.primary();
        stmt.addOption("BLOCK_FORMAT", expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.REPLICA_NUM)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.integerExpr();
        stmt.addOption("REPLICA_NUM", expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.TABLET_SIZE)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.integerExpr();
        stmt.addOption("TABLET_SIZE", expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.PCTFREE)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.integerExpr();
        stmt.addOption("PCTFREE", expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.USE_BLOOM_FILTER)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLExpr expr = this.exprParser.primary();
        stmt.addOption("USE_BLOOM_FILTER", expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.AUTO_INCREMENT)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("AUTO_INCREMENT", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("AVG_ROW_LENGTH")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("AVG_ROW_LENGTH", this.exprParser.expr());
        continue;
      }

      if (lexer.token() == Token.DEFAULT) {
        lexer.nextToken();
        parseTableOptionCharsetOrCollate(stmt);
        continue;
      }

      if (parseTableOptionCharsetOrCollate(stmt)) {
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.CHECKSUM)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("CHECKSUM", this.exprParser.expr());
        continue;
      }

      if (lexer.token() == Token.COMMENT) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.setComment(this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.CONNECTION)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("CONNECTION", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.DATA)) {
        lexer.nextToken();
        acceptIdentifier("DIRECTORY");
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("DATA DIRECTORY", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("DELAY_KEY_WRITE")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("DELAY_KEY_WRITE", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("FULLTEXT_DICT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("FULLTEXT_DICT", this.exprParser.charExpr());
        continue;
      }

      if (lexer.token() == Token.INDEX) {
        lexer.nextToken();
        acceptIdentifier("DIRECTORY");
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("INDEX DIRECTORY", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("INSERT_METHOD")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("INSERT_METHOD", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("KEY_BLOCK_SIZE")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("KEY_BLOCK_SIZE", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.MAX_ROWS)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("MAX_ROWS", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.MIN_ROWS)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("MIN_ROWS", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.PACK_KEYS)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("PACK_KEYS", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.PASSWORD)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("PASSWORD", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("ROW_FORMAT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("ROW_FORMAT", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("STATS_AUTO_RECALC")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        stmt.addOption("STATS_AUTO_RECALC", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("STATS_PERSISTENT")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        stmt.addOption("STATS_PERSISTENT", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("STATS_SAMPLE_PAGES")) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        stmt.addOption("STATS_SAMPLE_PAGES", this.exprParser.expr());
        continue;
      }

      if (lexer.token() == Token.UNION) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        accept(Token.LPAREN);
        SQLListExpr list = new SQLListExpr();
        this.exprParser.exprList(list.getItems(), list);
        stmt.addOption("UNION", list);
        accept(Token.RPAREN);
        continue;
      }

      if (lexer.token() == Token.TABLESPACE) {
        lexer.nextToken();

        MySqlCreateTableStatement.TableSpaceOption option = new MySqlCreateTableStatement.TableSpaceOption();
        option.setName(this.exprParser.name());

        if (lexer.identifierEquals("STORAGE")) {
          lexer.nextToken();
          option.setStorage(this.exprParser.name());
        }

        stmt.addOption("TABLESPACE", option);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.TABLEGROUP)) {
        lexer.nextToken();

        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }

        SQLName tableGroup = this.exprParser.name();
        stmt.setTableGroup(tableGroup);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.TYPE)) {
        lexer.nextToken();
        accept(Token.EQ);
        stmt.addOption("TYPE", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals("INDEX_ALL")) {
        lexer.nextToken();
        accept(Token.EQ);
        if (lexer.token() == Token.LITERAL_CHARS) {
          if ("Y".equalsIgnoreCase(lexer.stringVal())) {
            lexer.nextToken();
            stmt.addOption("INDEX_ALL", new SQLCharExpr("Y"));
          } else if ("N".equalsIgnoreCase(lexer.stringVal())) {
            lexer.nextToken();
            stmt.addOption("INDEX_ALL", new SQLCharExpr("N"));
          } else {
            throw new ParserException("INDEX_ALL accept parameter ['Y' or 'N'] only.");
          }
        }
        continue;
      }

      if (lexer.identifierEquals("RT_INDEX_ALL")) {
        lexer.nextToken();
        accept(Token.EQ);
        if (lexer.token() == Token.LITERAL_CHARS) {
          if ("Y".equalsIgnoreCase(lexer.stringVal())) {
            lexer.nextToken();
            stmt.addOption("RT_INDEX_ALL", new SQLCharExpr("Y"));
          } else if ("N".equalsIgnoreCase(lexer.stringVal())) {
            lexer.nextToken();
            stmt.addOption("RT_INDEX_ALL", new SQLCharExpr("N"));
          } else {
            throw new ParserException("RT_INDEX_ALL accepts parameter ['Y' or 'N'] only.");
          }
        }

        continue;
      }
      if (lexer.identifierEquals(FnvHash.Constants.ARCHIVE)) {
        lexer.nextToken();
        accept(Token.BY);
        acceptIdentifier("OSS");
        stmt.setArchiveBy(new SQLIdentifierExpr("OSS"));
        continue;
      }

      if (lexer.identifierEquals("STORAGE_TYPE")) {
        lexer.nextToken();
        accept(Token.EQ);
        stmt.addOption("STORAGE_TYPE", this.exprParser.charExpr());
        continue;
      }

      if (lexer.identifierEquals("STORAGE_POLICY")) {
        lexer.nextToken();
        accept(Token.EQ);
        stmt.addOption("STORAGE_POLICY", this.exprParser.charExpr());
        continue;
      }

      if (lexer.identifierEquals("HOT_PARTITION_COUNT")) {
        lexer.nextToken();
        accept(Token.EQ);
        try {
          stmt.addOption("HOT_PARTITION_COUNT", this.exprParser.integerExpr());
        } catch (Exception e) {
          throw new ParserException("only integer number is supported for hot_partition_count");
        }
        continue;
      }

      if (lexer.identifierEquals("TABLE_PROPERTIES")) {
        lexer.nextToken();
        accept(Token.EQ);
        stmt.addOption("TABLE_PROPERTIES", exprParser.charExpr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.ENCRYPTION)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("ENCRYPTION", this.exprParser.expr());
        continue;
      } else if (lexer.identifierEquals(FnvHash.Constants.COMPRESSION)) {
        lexer.nextToken();
        if (lexer.token() == Token.EQ) {
          lexer.nextToken();
        }
        stmt.addOption("COMPRESSION", this.exprParser.expr());
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.CLUSTERED)) {
        lexer.nextToken();
        accept(Token.BY);
        accept(Token.LPAREN);
        for (; ; ) {
          SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
          stmt.addClusteredByItem(item);
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          }
          break;
        }
        accept(Token.RPAREN);
        continue;
      }

      if (lexer.token() == Token.PARTITION) {
        SQLPartitionBy partitionClause = parsePartitionBy();
        stmt.setPartitioning(partitionClause);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.LOCAL)) {
        SQLPartitionBy localPartitionClause = parseLocalPartitionBy();
        stmt.setLocalPartitioning(localPartitionClause);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.BROADCAST)) {
        lexer.nextToken();
        stmt.setBroadCast(true);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.DISTRIBUTE) || lexer.identifierEquals(FnvHash.Constants.DISTRIBUTED)) {
        lexer.nextToken();
        accept(Token.BY);
        if (lexer.identifierEquals(FnvHash.Constants.HASH)) {
          lexer.nextToken();
          accept(Token.LPAREN);
          for (; ; ) {
            SQLName name = this.exprParser.name();
            stmt.getDistributeBy().add(name);
            if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
              continue;
            }
            break;
          }
          accept(Token.RPAREN);
          stmt.setDistributeByType(new SQLIdentifierExpr("HASH"));
        } else if (lexer.identifierEquals(FnvHash.Constants.DUPLICATE)) {
          lexer.nextToken();
          accept(Token.LPAREN);
          for (; ; ) {
            SQLName name = this.exprParser.name();
            stmt.getDistributeBy().add(name);
            if (lexer.token() == Token.COMMA) {
              lexer.nextToken();
              continue;
            }
            break;
          }
          accept(Token.RPAREN);
          stmt.setDistributeByType(new SQLIdentifierExpr("DUPLICATE"));
        } else if (lexer.identifierEquals(FnvHash.Constants.BROADCAST)) {
          lexer.nextToken();
          stmt.setDistributeByType(new SQLIdentifierExpr("BROADCAST"));
        }
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.DBPARTITION)) {
        lexer.nextToken();
        accept(Token.BY);
        SQLExpr dbPartitoinBy = this.exprParser.primary();
        stmt.setDbPartitionBy(dbPartitoinBy);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.DBPARTITIONS)) {
        lexer.nextToken();
        SQLExpr dbPartitions = this.exprParser.primary();
        stmt.setDbPartitions(dbPartitions);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.TBPARTITION)) {
        lexer.nextToken();
        accept(Token.BY);
        SQLExpr expr = this.exprParser.expr();
        if (lexer.identifierEquals(FnvHash.Constants.STARTWITH)) {
          lexer.nextToken();
          SQLExpr start = this.exprParser.primary();
          acceptIdentifier("ENDWITH");
          SQLExpr end = this.exprParser.primary();
          expr = new SQLBetweenExpr(expr, start, end);
        }
        stmt.setTablePartitionBy(expr);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.TBPARTITIONS)) {
        lexer.nextToken();
        SQLExpr tbPartitions = this.exprParser.primary();
        stmt.setTablePartitions(tbPartitions);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.EXTPARTITION)) {
        lexer.nextToken();
        accept(Token.LPAREN);

        MySqlExtPartition partitionDef = new MySqlExtPartition();

        for (; ; ) {
          MySqlExtPartition.Item item = new MySqlExtPartition.Item();

          if (lexer.identifierEquals(FnvHash.Constants.DBPARTITION)) {
            lexer.nextToken();
            SQLName name = this.exprParser.name();
            item.setDbPartition(name);
            accept(Token.BY);
            SQLExpr value = this.exprParser.primary();
            item.setDbPartitionBy(value);
          }

          if (lexer.identifierEquals(FnvHash.Constants.TBPARTITION)) {
            lexer.nextToken();
            SQLName name = this.exprParser.name();
            item.setTbPartition(name);
            accept(Token.BY);
            SQLExpr value = this.exprParser.primary();
            item.setTbPartitionBy(value);
          }

          item.setParent(partitionDef);
          partitionDef.getItems().add(item);

          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          } else {
            break;
          }
        }
        accept(Token.RPAREN);
        stmt.setExPartition(partitionDef);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.OPTIONS)) {
        lexer.nextToken();
        accept(Token.LPAREN);

        stmt.putAttribute("ads.options", Boolean.TRUE);
        for (; ; ) {
          String name = lexer.stringVal();
          lexer.nextToken();
          accept(Token.EQ);
          SQLExpr value = this.exprParser.primary();
          stmt.addOption(name, value);
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          }
          break;
        }

        accept(Token.RPAREN);
        continue;
      }

      if (lexer.identifierEquals(FnvHash.Constants.STORED)) {
        lexer.nextToken();
        accept(Token.BY);
        SQLName name = this.exprParser.name();
        stmt.setStoredBy(name);
      }

      if (lexer.token() == Token.WITH) {
        lexer.nextToken();
        accept(Token.LPAREN);

        for (; ; ) {
          String name = lexer.stringVal();
          lexer.nextToken();
          accept(Token.EQ);
          SQLName value = this.exprParser.name();
          stmt.getWith().put(name, value);
          if (lexer.token() == Token.COMMA) {
            lexer.nextToken();
            continue;
          }
          break;
        }

        accept(Token.RPAREN);
        continue;
      }

      if (lexer.token() == (Token.HINT)) {
        this.exprParser.parseHints(stmt.getOptionHints());
        continue;
      }

      // 扩展OceanBase option
      if ("organization".equalsIgnoreCase(lexer.stringVal())) {

        continue;
      }

      if ("AUTO_INCREMENT_MODE".equalsIgnoreCase(lexer.stringVal())) {
        lexer.nextToken();
        // [EQ] 可选
        if (lexer.token() == Token.EQ) {
          accept(Token.EQ);
        }
        stmt.addOption("AUTO_INCREMENT_MODE", this.exprParser.charExpr());
        continue;
      }


      break;
    }

    if (lexer.token() == (Token.ON)) {
      throw new ParserException("TODO. " + lexer.info());
    }

    if (lexer.token() == Token.REPLACE) {
      lexer.nextToken();
      stmt.setReplace(true);
    } else if (lexer.identifierEquals("IGNORE")) {
      lexer.nextToken();
      stmt.setIgnore(true);
    } else if (lexer.identifierEquals("SINGLE")) { // for polardb-x
      lexer.nextToken();
      stmt.setSingle(true);
    }

    if (lexer.token() == (Token.AS)) {
      lexer.nextToken();

      if (lexer.token() == Token.LPAREN) {
        lexer.nextToken();
        SQLSelect query = new MySqlSelectParser(this.exprParser).select();
        stmt.setSelect(query);
        accept(Token.RPAREN);
      }
      if (lexer.token() == Token.WITH) {
        stmt.setWithSelect(this.parseWith());
      }
    }

    SQLCommentHint hint = null;
    if (lexer.token() == Token.HINT) {
      hint = this.exprParser.parseHint();
    }

    if (lexer.token() == (Token.SELECT)) {
      SQLSelect query = new MySqlSelectParser(this.exprParser).select();
      if (hint != null) {
        query.setHeadHint(hint);
      }
      stmt.setSelect(query);

      if (lexer.token() == Token.WITH) {
        lexer.nextToken();
        if (lexer.identifierEquals(FnvHash.Constants.NO)) {
          lexer.nextToken();
          acceptIdentifier("DATA");
          stmt.setWithData(false);
        } else {
          acceptIdentifier("DATA");
          stmt.setWithData(true);
        }
      }
    }

    while (lexer.token() == (Token.HINT)) {
      this.exprParser.parseHints(stmt.getOptionHints());
    }
    return stmt;
  }

  private boolean parseTableOptionCharsetOrCollate(MySqlCreateTableStatement stmt) {
    if (lexer.identifierEquals("CHARACTER")) {
      lexer.nextToken();
      accept(Token.SET);
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      SQLExpr charset;
      if (lexer.token() == Token.IDENTIFIER) {
        charset = new SQLIdentifierExpr(lexer.stringVal());
        lexer.nextToken();
      } else if (lexer.token() == Token.LITERAL_CHARS) {
        charset = new SQLCharExpr(lexer.stringVal());
        lexer.nextToken();
      } else {
        charset = this.exprParser.primary();
      }
      stmt.addOption("CHARACTER SET", charset);
      return true;
    }

    if (lexer.identifierEquals("CHARSET")) {
      lexer.nextToken();
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      SQLExpr charset;
      if (lexer.token() == Token.IDENTIFIER) {
        charset = new SQLIdentifierExpr(lexer.stringVal());
        lexer.nextToken();
      } else if (lexer.token() == Token.LITERAL_CHARS) {
        charset = new SQLCharExpr(lexer.stringVal());
        lexer.nextToken();
      } else {
        charset = this.exprParser.primary();
      }
      stmt.addOption("CHARSET", charset);
      return true;
    }

    if (lexer.identifierEquals("COLLATE")) {
      lexer.nextToken();
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      stmt.addOption("COLLATE", this.exprParser.expr());
      return true;
    }

    if (lexer.identifierEquals("LOCALITY")) {
      lexer.nextToken();
      if (lexer.token() == Token.EQ) {
        lexer.nextToken();
      }
      stmt.addOption("LOCALITY", this.exprParser.expr());
      return true;
    }

    return false;
  }
}
